package org.lecric.wind44.client.dialog;

import java.util.HashMap;
import java.util.List;

import org.lecric.wind44.client.SpotUtils;
import org.lecric.wind44.client.model.ISpot;

import com.allen_sauer.gwt.dnd.client.DragContext;
import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.allen_sauer.gwt.dnd.client.drop.AbstractPositioningDropController;
import com.allen_sauer.gwt.dnd.client.drop.BoundaryDropController;
import com.allen_sauer.gwt.dnd.client.util.CoordinateLocation;
import com.allen_sauer.gwt.dnd.client.util.DOMUtil;
import com.allen_sauer.gwt.dnd.client.util.Location;
import com.allen_sauer.gwt.dnd.client.util.LocationWidgetComparator;
import com.allen_sauer.gwt.dnd.client.util.WidgetLocation;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.IndexedPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * @author Lecric
 */
public abstract class ReorderDialog extends DialogBox
{
	private static final class FlexTableRowDropController extends AbstractPositioningDropController {

		private static final String CSS_DEMO_TABLE_POSITIONER = "demo-table-positioner";

		private FlexTable flexTable;

		private IndexedPanel flexTableRowsAsIndexPanel = new IndexedPanel() {

			public void add(Widget w) {
				throw new UnsupportedOperationException();
			}

			public Widget getWidget(int index) {
				return flexTable.getWidget(index, 0);
			}

			public int getWidgetCount() {
				return flexTable.getRowCount();
			}

			public int getWidgetIndex(Widget child) {
				throw new UnsupportedOperationException();
			}

			public void insert(Widget w, int beforeIndex) {
				throw new UnsupportedOperationException();
			}

			public boolean remove(int index) {
				throw new UnsupportedOperationException();
			}
		};

		private Widget positioner = null;

		private int targetRow;

		public FlexTableRowDropController(FlexTable flexTable) {
			super(flexTable);
			this.flexTable = flexTable;
		}

		@Override
		public void onDrop(DragContext context) {
			FlexTableRowDragController trDragController = (FlexTableRowDragController) context.dragController;
			FlexTableUtil.moveRow(trDragController.getDraggableTable(), flexTable,
					trDragController.getDragRow(), targetRow + 1);
			super.onDrop(context);
		}

		@Override
		public void onEnter(DragContext context) {
			super.onEnter(context);
			positioner = newPositioner(context);
		}

		@Override
		public void onLeave(DragContext context) {
			positioner.removeFromParent();
			positioner = null;
			super.onLeave(context);
		}

		@Override
		public void onMove(DragContext context) {
			super.onMove(context);
			targetRow = DOMUtil.findIntersect(flexTableRowsAsIndexPanel, new CoordinateLocation(
					context.mouseX, context.mouseY), LocationWidgetComparator.BOTTOM_HALF_COMPARATOR) - 1;

			if (flexTable.getRowCount() > 0) {
				Widget w = flexTable.getWidget(targetRow == -1 ? 0 : targetRow, 0);
				Location widgetLocation = new WidgetLocation(w, context.boundaryPanel);
				Location tableLocation = new WidgetLocation(flexTable, context.boundaryPanel);
				context.boundaryPanel.add(positioner, tableLocation.getLeft(), widgetLocation.getTop()
						+ (targetRow == -1 ? 0 : w.getOffsetHeight()));
			}
		}

		Widget newPositioner(DragContext context) {
			Widget p = new SimplePanel();
			p.addStyleName(CSS_DEMO_TABLE_POSITIONER);
			p.setPixelSize(flexTable.getOffsetWidth(), 1);
			return p;
		}
	}

	/**
	 * Allows table rows to dragged by their handle.
	 */
	private static final class FlexTableRowDragController extends PickupDragController {

		  private static final String CSS_DEMO_FLEX_TABLE_ROW_EXAMPLE_TABLE_PROXY = "demo-FlexTableRowExample-table-proxy";

		  private FlexTable draggableTable;

		  private int dragRow;

		  public FlexTableRowDragController(AbsolutePanel boundaryPanel) {
		    super(boundaryPanel, false);
		    setBehaviorDragProxy(true);
		    setBehaviorMultipleSelection(false);
		  }

		  @Override
		  public void dragEnd() {
		    super.dragEnd();

		    // cleanup
		    draggableTable = null;
		  }

		  @Override
		  public void setBehaviorDragProxy(boolean dragProxyEnabled) {
		    if (!dragProxyEnabled) {
		      // TODO implement drag proxy behavior
		      throw new IllegalArgumentException();
		    }
		    super.setBehaviorDragProxy(dragProxyEnabled);
		  }

		  @Override
		  protected BoundaryDropController newBoundaryDropController(AbsolutePanel boundaryPanel,
		      boolean allowDroppingOnBoundaryPanel) {
		    if (allowDroppingOnBoundaryPanel) {
		      throw new IllegalArgumentException();
		    }
		    return super.newBoundaryDropController(boundaryPanel, allowDroppingOnBoundaryPanel);
		  }

		  @Override
		  protected Widget newDragProxy(DragContext context) {
		    FlexTable proxy;
		    proxy = new FlexTable();
		    proxy.addStyleName(CSS_DEMO_FLEX_TABLE_ROW_EXAMPLE_TABLE_PROXY);
		    draggableTable = (FlexTable) context.draggable.getParent();
		    dragRow = getWidgetRow(context.draggable, draggableTable);
		    FlexTableUtil.copyRow(draggableTable, proxy, dragRow, 0);
		    return proxy;
		  }

		  FlexTable getDraggableTable() {
		    return draggableTable;
		  }

		  int getDragRow() {
		    return dragRow;
		  }

		  private int getWidgetRow(Widget widget, FlexTable table) {
		    for (int row = 0; row < table.getRowCount(); row++) {
		      for (int col = 0; col < table.getCellCount(row); col++) {
		        Widget w = table.getWidget(row, col);
		        if (w == widget) {
		          return row;
		        }
		      }
		    }
		    throw new RuntimeException("Unable to determine widget row");
		  }
		}
	
	
	/**
	 * Utility class to manipulate {@link FlexTable FlexTables}.
	 */
	private static class FlexTableUtil {

	  /**
	   * Copy an entire FlexTable from one FlexTable to another. Each element is copied by creating a
	   * new {@link HTML} widget by calling {@link FlexTable#getHTML(int, int)} on the source table.
	   * 
	   * @param sourceTable the FlexTable to copy a row from
	   * @param targetTable the FlexTable to copy a row to
	   * @param sourceRow the index of the source row
	   * @param targetRow the index before which to insert the copied row
	   */
	  public static void copyRow(FlexTable sourceTable, FlexTable targetTable, int sourceRow,
	      int targetRow) {
	    targetTable.insertRow(targetRow);
	    for (int col = 0; col < sourceTable.getCellCount(sourceRow); col++) {
	      HTML html = new HTML(sourceTable.getHTML(sourceRow, col));
	      targetTable.setWidget(targetRow, col, html);
	    }
	    copyRowStyle(sourceTable, targetTable, sourceRow, targetRow);
	  }

	  /**
	   * Move an entire FlexTable from one FlexTable to another. Elements are moved by attempting to
	   * call {@link FlexTable#getWidget(int, int)} on the source table. If no widget is found (because
	   * <code>null</code> is returned), a new {@link HTML} is created instead by calling
	   * {@link FlexTable#getHTML(int, int)} on the source table.
	   * 
	   * @param sourceTable the FlexTable to move a row from
	   * @param targetTable the FlexTable to move a row to
	   * @param sourceRow the index of the source row
	   * @param targetRow the index before which to insert the moved row
	   */
	  public static void moveRow(FlexTable sourceTable, FlexTable targetTable, int sourceRow,
	      int targetRow) {
	    if (sourceTable == targetTable && sourceRow >= targetRow) {
	      sourceRow++;
	    }
	    targetTable.insertRow(targetRow);
	    for (int col = 0; col < sourceTable.getCellCount(sourceRow); col++) {
	      Widget w = sourceTable.getWidget(sourceRow, col);
	      if (w != null) {
	        targetTable.setWidget(targetRow, col, w);
	      } else {
	        HTML html = new HTML(sourceTable.getHTML(sourceRow, col));
	        targetTable.setWidget(targetRow, col, html);
	      }
	    }
	    copyRowStyle(sourceTable, targetTable, sourceRow, targetRow);
	    sourceTable.removeRow(sourceRow);
	  }

	  /**
	   * Copies the CSS style of a source row to a target row.
	   * 
	   * @param sourceTable
	   * @param targetTable
	   * @param sourceRow
	   * @param targetRow
	   */
	  private static void copyRowStyle(FlexTable sourceTable, FlexTable targetTable, int sourceRow,
	      int targetRow) {
	    String rowStyle = sourceTable.getRowFormatter().getStyleName(sourceRow);
	    targetTable.getRowFormatter().setStyleName(targetRow, rowStyle);
	  }

	}
	
	/**
	 * Table to demonstrate draggable rows and columns.
	 */
	private static final class DemoFlexTable extends FlexTable {
		private HashMap<HTML, ISpot> cache=new HashMap<HTML, ISpot>();
	  /**
	   * Creates a FlexTable with the desired number of rows and columns, making each row draggable via
	   * the provided drag controller.
	   * 
	   * @param rows desired number of table rows
	   * @param cols desired number of table columns
	   * @param tableRowDragController the drag controller to enable dragging of table rows
	   */
	  public DemoFlexTable(List<? extends ISpot> spots, FlexTableRowDragController tableRowDragController) {
	    addStyleName("demo-flextable");
	    int row=0;
	    for(ISpot spot: SpotUtils.sort(spots)) {
	      HTML handle = new HTML(spot.getName());
	      cache.put(handle, spot);
	      handle.addStyleName("demo-drag-handle");
	      setWidget(row++, 0, handle);
	      tableRowDragController.makeDraggable(handle);
	    }
	  }
	}
	
	private Button okButton;
	private DemoFlexTable table1;

	public ReorderDialog(final List<? extends ISpot> spots, String title)
	{
		setText(title);
		VerticalPanel vpanel = new VerticalPanel();
//		HorizontalPanel hpanel1=new HorizontalPanel();
//		hpanel1.add(new Label("Name"));
		addStyleName("reorderDialog");

		AbsolutePanel tableExamplePanel = new AbsolutePanel();
		    tableExamplePanel.setPixelSize(450, 500);
		//		textBox = new TextBox();
		FlexTableRowDragController tableRowDragController = new FlexTableRowDragController(tableExamplePanel);
		table1 = new DemoFlexTable(spots, tableRowDragController);
//		table1.setSize("450px", "300x");
		tableExamplePanel.add(table1, 0, 0);
		FlexTableRowDropController flexTableRowDropController1 = new FlexTableRowDropController(table1);
		tableRowDragController.registerDropController(flexTableRowDropController1);

//		hpanel1.add(tableExamplePanel);
		vpanel.add(tableExamplePanel);
		HorizontalPanel hpanel2=new HorizontalPanel();
		okButton = new Button("OK");
		ClickHandler closeHandler = new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				setOrders();
				hide();
				validate();
			}
		};
		okButton.addClickHandler(closeHandler);
		hpanel2.add(okButton);
		Button cancelButton = new Button("Cancel");
		cancelButton.addClickHandler(closeHandler);
		hpanel2.add(cancelButton);
		vpanel.add(hpanel2);
		add(vpanel);
//		textBox.setFocus(true);
		center();
	}

	protected abstract void validate();

	public Button getOkButton() {
		return okButton;
	}
	
	public void setOrders()
	{
		int index=0;
		String str="Reorder result: ";
		for(int i=0; i < table1.getRowCount() ; i++)
		{
			HTML handle=(HTML) table1.getWidget(i, 0);
			ISpot spot = table1.cache.get(handle);
			str+=index + ". " + spot.getName() + ";";
			spot.setOrder(index++);
		}
		GWT.log(str, null);
	}

//	public String getName() {
//		return textBox.getText();
//	}
//
//	public void setName(String name)
//	{
//		textBox.setText(name);
//	}
}
